package commDao

import (
	"errors"
	"fmt"
	"reflect"
	"xxgame.com/CommApp/Util/goroutine"
	"xxgame.com/CommApp/Util/littleFunc"
	"xxgame.com/CommApp/Util/lock"
	"xxgame.com/CommApp/Util/log"
)

////////////// 对象容器实现
type PstEntityCtnIF interface {
	GetItem(key int) *PstEntity
	AddItem(obj *PstEntity) bool
	Remove(obj *PstEntity) bool
	GetAllItem() map[int]*PstEntity
	GetAllKeys() []int
	Load() error
	Save() error
}

////////////// 对象容器基类实现
type PstEntityCtnBase struct {
	PstEntityCtnIF
	cond        CommEntityIF       // 查询条件对象
	list        interface{}        // 待查询结果（数据结构）
	keys        []string           // 多级索引Key名称列表
	dao         *CommDao           // 数据库访问对象
	uid         int64              // 用户uid
	dKeyMapItem map[int]*PstEntity // 容器索引Map
	ctnTag      string             // 容器标签
}

// 容器初始化
// 		cond	CommEntityIF	查询容器数据的条件
//		dao		CommDao			访问数据库通用对象
//		list	interface{}		待查询数据对象列表（传入该参数，主要提供结构）
//		keyList	...string		传入子对象key名称列表（如：["uid", "equip_id"]）
func (this *PstEntityCtnBase) Init(cond CommEntityIF, dao *CommDao,
					list interface{}, uid int64, keyList...string) interface{} {
	this.cond = cond
	this.dao = dao
	this.list = list
	this.keys = keyList
	this.uid = uid
	this.dKeyMapItem = map[int]*PstEntity{}
	ctnTag, _ := GetTag(cond, nil)
	this.ctnTag = ctnTag
	return this
}

// 容器数据加载
func (this *PstEntityCtnBase) Load() error {
	if 2 == GetPstEntityImpVer() {
		lock.MgrCtn().Lock(this.ctnTag)
		defer lock.MgrCtn().UnLock(this.ctnTag)
	}
	err := this.dao.FindByWhere(this.cond, this.list)
	if nil != err {
		return err
	}
	if nil == this.list {
		return errors.New("list is nil")
	}
	rfObjListPtr := reflect.ValueOf(this.list)
	rfObjList := rfObjListPtr.Elem()
	if reflect.Slice != rfObjList.Kind() {
		return errors.New("list is not list type")
	}
	curGoid := goroutine.GetID()
	// this.list 经过 Load 之后，由 *[]*entity 转换成 []*entity的形式存储
	this.list = rfObjList.Interface()
	objList := littleFunc.SliceTypeChange(this.list, []CommEntityIF{}).([]CommEntityIF)
	for _, obj := range objList {
		rfObj := reflect.ValueOf(obj)
		rfTyp := reflect.TypeOf(rfObj.Elem().Interface())
		newRfObj := reflect.New(rfTyp)
		// 创建单个obj的查询条件cond
		for _, keyName := range this.keys {
			rfAttrVal := rfObj.Elem().FieldByName(keyName)
			if !rfAttrVal.IsValid() {
				panic(fmt.Sprintf("rfObj have not found %s key", keyName))
			}
			rfAttValDst := newRfObj.Elem().FieldByName(keyName)
			if !rfAttValDst.IsValid() {
				panic(fmt.Sprintf("newRfObj have not found %s key", keyName))
			}
			rfAttValDst.Set(rfAttrVal)
		}
		cond := newRfObj.Interface().(CommEntityIF)
		pstObj := NewPstEntity(obj, this.dao, cond, this.uid, "")
		if 2 == GetPstEntityImpVer() {
			// 来到这里有两种可能，一种是已找到cache，一种是未找到cache
			if !pstObj.IsInitData() { // 未找到cache（二次模糊匹配）
				pstObj_t := pstObj.FuzzyMatch()
				if nil != pstObj_t { // 匹配成功，直接使用cache
					pstObj.RebindObject(pstObj_t.(*PstEntity).GetObj())
				} else { // 匹配失败，直接绑定容器批量加载回来的obj使用
					pstObj.RebindObject(obj)
				}
				pstObj.SetLoaded()
			}
			pstObj.Load() // 这里仅仅执行上锁逻辑，并非加载数据（todo：优化）
		} else {
			// 容器批量加载数据，故这里无需调用pstObj.Load()
			pstObj.RebindObject(obj)
			pstObj.SetLoaded()
		}

		idx := rfObj.Elem().FieldByName("ID").Int()
		// 持久化子对象添加到容器对象
		pstObj.SetID(int(idx))
		this.AddItem(pstObj)

		if 2 == GetPstEntityImpVer() {
			log.PST.Infof("查询记录成功 PstEntityCtnBase.Load(), curGoid=%+v, " +
				"TableName=%+v, rowCtn=%+v, cond=%+v, obj=%+v", curGoid, this.cond.TableName(),
				len(objList), this.cond, obj)
		}
	}
	return nil
}

// 容器数据持久化
func (this *PstEntityCtnBase) Save() error {
	for _, pstObj := range(this.dKeyMapItem) {
		pstObj.Save() // pstObj内部会判断是否脏，是则执行持久化
		if 0 < pstObj.GetID() { // 并且从容器中移除
			delete(this.dKeyMapItem, pstObj.GetID())
		}
	}
	return nil
}

// 获取指定的实例
func (this *PstEntityCtnBase) GetItem(key int) *PstEntity {
	pstObj, ok := this.dKeyMapItem[key]
	if !ok {
		return nil
	}
	return pstObj
}

// 往容器添加实例
func (this *PstEntityCtnBase) AddItem(pstObj *PstEntity) bool {
	idx := pstObj.GetID()
	if 0 >= idx {
		return false
	}
	if nil != this.GetItem(idx) {
		return false
	}
	this.dKeyMapItem[idx] = pstObj
	return true
}

// 从容器中移除实例（仅仅从内存中移除，后续考虑是否从数据库中移除）
func (this *PstEntityCtnBase) Remove(pstObj *PstEntity) bool {
	id := pstObj.GetID()
	if 0 >= id {
		return false
	}
	if nil == this.GetItem(id) {
		return false
	}
	//pstObj.Remove()
	delete(this.dKeyMapItem, id)
	return true
}

// 返回容器实例列表
func (this *PstEntityCtnBase) GetAllItem() map[int]*PstEntity {
	if 0 >= len(this.dKeyMapItem) {
		return nil
	}
	return this.dKeyMapItem
}

// 返回容器实例ID列表
func (this *PstEntityCtnBase) GetAllKeys() []int {
	if 0 >= len(this.dKeyMapItem) {
		return nil
	}
	idList := []int{}
	for idx, _ := range this.dKeyMapItem {
		idList = append(idList, idx)
	}
	return idList
}

////////////// 对象容器实现
type PstEntityCtn struct {
	*PstEntityCtnBase
}

func (this *PstEntityCtn) Init(cond CommEntityIF, dao *CommDao,
					list interface{}, uid int64, keyList...string) interface{} {
	this.PstEntityCtnBase.Init(cond, dao, list, uid, keyList...)
	return this
}

// 创建对象管理器
// 	参数说明：
//		cond：interface{} 容器查询子项条件 *entity
//		dao: *commDao 通用访问对象
//		list: *[]*entity 子项列表结构
//		keyList: 子项查询字段名字（eg："Uid", "HeroID", "SkillID"）
func NewPstCtn(cond CommEntityIF, dao *CommDao,
	list interface{}, uid int64, keyList...string) *PstEntityCtn {
	ctnObject := &PstEntityCtn{
		PstEntityCtnBase: &PstEntityCtnBase{
		},
	}
	return ctnObject.Init(cond, dao, list, uid, keyList...).(*PstEntityCtn)
}